const jsonwebtoekn = require('jsonwebtoken')
const User = require('../models/users')
const Question = require('../models/questions')
const Answer = require('../models/answers')
const { SECRET } = require('../config')

class Users {
  async login(ctx) {
    ctx.verifyParams({
      name: { type: 'string', required: true },
      password: { type: 'string', required: true }
    })
    const user = await User.findOne(ctx.request.body)
    if (!user) {
      ctx.throw(401, '用户或密码不正确')
    }
    const { _id, name } = user
    const token = jsonwebtoekn.sign({ _id, name }, SECRET, { expiresIn: '1d' })
    ctx.body = { token }
  }

  async find(ctx) {
    // 每页大小
    const pageSize = Math.max((ctx.query.page_size || 10) * 1, 1)
    // 当前页
    const current = ctx.query.current * 1
    // 偏移量
    const offset = Math.max(current - 1, 0) * pageSize
    // 查询参数
    const params = { name: new RegExp(ctx.query.q) }

    ctx.body = await User.find(params).limit(pageSize).skip(offset)
  }

  async findById(ctx) {
    const { fields = '' } = ctx.query
    const arr = fields.split(';')
    // example: employments;educations --> employments educations
    let selectStr = ''
    // example: employments;educations --> employments.company employments.job educations.school educations.major
    let populateStr = ''
    for (let i = 0; i < arr.length; i++) {
      if (arr[i]) {
        selectStr += ` ${arr[i]}`
        if (arr[i] === 'employments') {
          populateStr += ' employments.company employments.job'
        } else if (arr[i] === 'educations') {
          populateStr += ' educations.school educations.major'
        } else {
          populateStr += ` ${arr[i]}`
        }
      }
    }
    const user = await User.findById(ctx.params.id)
      .select(selectStr.trim())
      .populate(populateStr.trim())
    if (!user) {
      ctx.throw(404, '用户不存在')
    }
    ctx.body = user
  }

  async create(ctx) {
    ctx.verifyParams({
      name: { type: 'string', required: true },
      password: { type: 'string', required: true }
    })
    // 判断用户唯一性
    const repeatedUser = await User.findOne({ name: ctx.request.body.name })
    if (repeatedUser) {
      ctx.throw(409, '用户已经存在')
    }
    const user = await new User(ctx.request.body).save()
    ctx.body = user
  }

  async update(ctx) {
    // 字段校验
    ctx.verifyParams({
      name: { type: 'string', required: false },
      password: { type: 'string', required: false },
      avatar_url: { type: 'string', required: false },
      gender: { type: 'string', required: false },
      headline: { type: 'string', required: false },
      locations: { type: 'array', itemType: 'string', required: false },
      business: { type: 'string', required: false },
      employments: { type: 'array', itemType: 'object', required: false },
      educations: { type: 'array', itemType: 'object', required: false }
    })
    const user = await User.findByIdAndUpdate(ctx.params.id, ctx.request.body, {
      new: true
    })
    if (!user) {
      ctx.throw(404, '该用户不存在')
    }
    ctx.body = user
  }

  async delete(ctx) {
    const user = await User.findOneAndDelete(ctx.params.id)
    if (!user) {
      ctx.throw(404, '该用户不存在')
    }
    // 根据 RESTFUL 规范删除返回状态码 204 没有内容
    ctx.status = 204
  }

  async listFollowing(ctx) {
    // NOTE: 当在一个文档中存储另一个文档的引用时，populate 可以用来取回被引用的文档的详细信息。这样可以避免在查询数据时进行多次查询，提高查询效率。
    const user = await User.findById(ctx.params.id)
      .select('following')
      .populate('following')
    if (!user) {
      ctx.throw(404)
    }
    ctx.body = user.following
  }

  async follow(ctx) {
    const me = await User.findById(ctx.state.user._id).select('following')
    if (!me.following.map((id) => id.toString()).includes(ctx.params.id)) {
      me.following.push(ctx.params.id)
      me.save()
    }
    ctx.status = 204
  }

  async unfollow(ctx) {
    const me = await User.findById(ctx.state.user._id).select('following')
    const ids = me.following.map((id) => id.toString())
    const index = ids.indexOf(ctx.params.id)
    if (index > -1) {
      me.following.splice(index, 1)
      me.save()
    }
    ctx.status = 204
  }

  async listFollowers(ctx) {
    const users = await User.find({ following: ctx.params.id })
    ctx.body = users
  }

  async followTopic(ctx) {
    const me = await User.findById(ctx.state.user._id).select('followingTopics')
    if (
      !me.followingTopics.map((id) => id.toString()).includes(ctx.params.id)
    ) {
      me.followingTopics.push(ctx.params.id)
      me.save()
    }
    ctx.status = 204
  }

  async unfollowTopic(ctx) {
    const me = await User.findById(ctx.state.user._id).select('followingTopics')
    const ids = me.followingTopics.map((id) => id.toString())
    const index = ids.indexOf(ctx.params.id)
    if (index > -1) {
      me.followingTopics.splice(index, 1)
      me.save()
    }
    ctx.status = 204
  }

  async listFollowTopic(ctx) {
    const user = await User.findById(ctx.params.id)
      .select('followingTopics')
      .populate('followingTopics')
    if (!user) {
      ctx.throw(404)
    }
    ctx.body = user.followingTopics
  }

  async listQuestions(ctx) {
    const questions = await Question.find({ questioner: ctx.params.id })
    ctx.body = questions
  }

  async likeAnswer(ctx, next) {
    const me = await User.findById(ctx.state.user._id).select('likingAnswers')
    if (!me.likingAnswers.map((id) => id.toString()).includes(ctx.params.id)) {
      me.likingAnswers.push(ctx.params.id)
      me.save()
      await Answer.findByIdAndUpdate(ctx.params.id, { $inc: { voteCount: 1 } })
    }
    ctx.status = 204
    await next()
  }

  async unlikeAnswer(ctx) {
    const me = await User.findById(ctx.state.user._id).select('likingAnswers')
    const ids = me.likingAnswers.map((id) => id.toString())
    const index = ids.indexOf(ctx.params.id)
    if (index > -1) {
      me.likingAnswers.splice(index, 1)
      me.save()
      await Answer.findByIdAndUpdate(ctx.params.id, { $inc: { voteCount: -1 } })
    }
    ctx.status = 204
  }

  async listLikingAnswers(ctx) {
    const user = await User.findById(ctx.params.id)
      .select('likingAnswers')
      .populate('likingAnswers')
    if (!user) {
      ctx.throw(404)
    }
    ctx.body = user.likingAnswers
  }

  async dislikeAnswer(ctx, next) {
    const me = await User.findById(ctx.state.user._id).select(
      'dislikingAnswers'
    )
    if (
      !me.dislikingAnswers.map((id) => id.toString()).includes(ctx.params.id)
    ) {
      me.dislikingAnswers.push(ctx.params.id)
      me.save()
    }
    ctx.status = 204
    await next()
  }

  async unDislikeAnswer(ctx) {
    const me = await User.findById(ctx.state.user._id).select(
      'dislikingAnswers'
    )
    const ids = me.dislikingAnswers.map((id) => id.toString())
    const index = ids.indexOf(ctx.params.id)
    if (index > -1) {
      me.dislikingAnswers.splice(index, 1)
      me.save()
    }
    ctx.status = 204
  }

  async listDisLikingAnswers(ctx) {
    const user = await User.findById(ctx.params.id)
      .select('dislikingAnswers')
      .populate('dislikingAnswers')
    if (!user) {
      ctx.throw(404)
    }
    ctx.body = user.dislikingAnswers
  }

  async collectingAnswer(ctx, next) {
    const me = await User.findById(ctx.state.user._id).select(
      'collectingAnswers'
    )
    if (
      !me.collectingAnswers.map((id) => id.toString()).includes(ctx.params.id)
    ) {
      me.collectingAnswers.push(ctx.params.id)
      me.save()
    }
    ctx.status = 204
    await next()
  }

  async unCollectingAnswer(ctx) {
    const me = await User.findById(ctx.state.user._id).select(
      'collectingAnswers'
    )
    const ids = me.collectingAnswers.map((id) => id.toString())
    const index = ids.indexOf(ctx.params.id)
    if (index > -1) {
      me.collectingAnswers.splice(index, 1)
      me.save()
    }
    ctx.status = 204
  }

  async listCollectingAnswers(ctx) {
    const user = await User.findById(ctx.params.id)
      .select('collectingAnswers')
      .populate('collectingAnswers')
    if (!user) {
      ctx.throw(404)
    }
    ctx.body = user.collectingAnswers
  }
}

module.exports = new Users()
